ShaderGraph検証 : スクリーン
はじめに
今回、ShaderGraphのスクリーン合成の挙動を検証してみました。
BlendノードのModeをScreenに設定することでスクリーン合成が使えます。
https://gyazo.com/411af631be1f0408fd4bb08b4550e09b
1. 数式の考察
ShaderGraph上でスクリーン合成の挙動を観察する前に、スクリーン合成の数式を見ていきたいと思います。
1.0 スクリーン合成の計算式
スクリーン合成は以下のような式で計算できるそうです。
$ C = 1 - (1 - A)(1 - B)
$ A : 元から描画されている色 (0 \leqq A \leqq 1)
$ B : 上から重ねる色 (0 \leqq B \leqq 1)
$ C : AとBを合成した結果の色
1.1 スクリーン合成の特徴
「Cは白飛びしない」という特徴があります。
これを数式で書くと以下のようになります。
$ 0 \leqq C \leqq 1
0: 完全な黒色 (RGB = (0, 0, 0))
1: 完全な白色 (RGB = (1, 1, 1))
また、「CはA, Bよりも明るくなる」という特徴があります。
これを数式で書くと以下のようになります。
$ \begin{cases} C \geqq A \\C \geqq B \end{cases}
1.2 Cは0~1の範囲に収まる
「スクリーン合成後の色Cは 0 ~ 1の値に収まる」ことを数式から導いてみます。
これを示すためには以下の式が証明できれば良さそうです。
$ 0 \leqq C \leqq 1
証明)
$ 0 \leqq A \leqq 1 より、
$ 1 \geqq (1 - A) \geqq 0
$ \Rarr 0 \leqq (1 - A) \leqq 1 ・・・(1)
$ 0 \leqq B \leqq 1から、
$ 1 \geqq (1 - B) \geqq 0
$ \Rarr 0 \leqq (1 - B) \leqq 1 ・・・(2)
ここで、式(1)の各項に(1-B)を乗算してみます。(1-B)は非負なので不等号の向きは変化しません。(式(2
$ 0・(1 - B) \leqq (1 - A)・(1 - B) \leqq 1・(1 - B)
不等式の各項を1から減算するように式変形します。(各項の符号を反転させているので不等号の向きも反転します)
$ 1 - 0 \geqq 1 - (1 - A)・(1 - B) \geqq 1 - (1 - B)
$ \Rarr 1 \geqq 1 - (1 - A)・(1 - B) \geqq B
ここで、$ C = 1 - (1-A)・(1-B)なので、以下のように書くことができます。
$ 1 \geqq C \geqq B
$ \Rarr B \leqq C \leqq 1 ・・・(3)
また、Bについては $ B \geqq 0が成立するため、以下が成立します。
$ 0 \leqq B \leqq C \leqq 1
$ \Rarr 0 \leqq C \leqq 1
Cは0~1の範囲に収まっています。(証明完了)
1.3 BとCを比較してみる
先ほど、以下のような不等式(3)を導出しました。
$ B \leqq C \leqq 1 ・・・(3)
$ C \geqq B から、BよりCの方が明るいと言えます。
1.4 AとCを比較してみる
次に、AよりCの方が明るいということを数式から導出してみます。($ C \geqq A を証明してみます)
証明)
先ほど、以下のような不等式(1), (2)を導出しました。
$ 0 \leqq (1 - A) \leqq 1 ・・・(1)
$ 0 \leqq (1 - B) \leqq 1 ・・・(2)
ここで、式(2)の各項に(1-A)を乗算します。 式(1)より(1-A)は非負であることが分かるため、不等号の向きは変わりません。
$ 0・(1-A) \leqq (1 - B)・(1-A) \leqq 1・(1-A)
$ \Rarr 0 \leqq (1-B)(1-A) \leqq (1 - A)
不等式の各項を1から減算するように式変形します。(各項の符号を反転させているので不等号の向きも反転します)
$ 1 - 0 \geqq 1 - (1-B)(1-A) \geqq 1 - (1 - A)
$ \Rarr 1 \geqq 1 - (1-B)(1-A) \geqq A ・・・(5)
ここで、$ C = 1 - (1-B)(1-A)より式(5)は以下のように書くことができます。
$ 1 \geqq C \geqq A
$ C \geqq A から、AよりCの方が明るいと言えます。(証明完了)
1.5 まとめ
1.2より、Cは 0~1の範囲に収まっている(白飛びしない)ことを示すことができました。(A,Bが0~1に収まっている場合)
$ 0 \leqq C \leqq 1
1.3と1.4より、合成前の色A, Bより 合成後の色 C の方が明るいことを示すことができました。(A,Bが0~1に収まっている場合)
$ \begin{cases} C \geqq A \\C \geqq B \end{cases}
2 スクリーン合成の内部実装
ShaderGraphのスクリーン合成の内部実装は以下のようになっています。
code:Screen(glsl)
void Unity_Blend_Screen_float4(float4 Base, float4 Blend, float Opacity, out float4 Out)
{
Out = 1.0 - (1.0 - Blend) * (1.0 - Base);
Out = lerp(Base, Out, Opacity);
}
3. シェーダーグラフで検証
実際にShaderGraphを使ってScreenがどういったふるまいをするのかを観察してみました。
環境
Unity2020.1.0a14
ShaderGraph (Version 7.1.1)
Universal RP (Version 7.1.1)]
検証1 : A = 0, B = 1
A = 0, B = 1として Blendノード(Mode = Screen)へ入力してみました。 結果は白色になっています。
https://gyazo.com/26882bd49efb1be1fa99f61ed7efba8c
Screenブレンドは $ C = 1 - (1-A)(1-B)での計算することができ、これに A = 0, B = 1を代入すると
$ C = 1 - (1 - 0)(1 - 1) = 1 - 1 ・ 0 = 1となります。
C = 1 (白色)となり、実際の結果と計算結果が一致しました。
検証2 : A = 1, B = 0
A = 1, B = 0として Blendノード(Mode = Screen)へ入力してみました。 結果は白色になっています。
https://gyazo.com/be999db63469b8f723398fa1b3a29da3
Screenブレンドは $ C = 1 - (1-A)(1-B)での計算することができ、これに A = 1, B = 0を代入すると
$ C = 1 - (1 - 1)(1 - 0) = 1 - 0 ・ 1 = 1となります。
C = 1 (白色)となり、実際の結果と計算結果が一致しました。
検証3: A = 0.3, B = 0.5
A = 0.3, B = 0.5として Blendノード(Mode = Screen)へ入力してみました。 合成結果はA, Bより明るくなっています。
https://gyazo.com/1889474fcc1b1c7d93118b4358ae64e0
Screenブレンドは $ C = 1 - (1-A)(1-B)での計算することができ、これに A = 0.3, B = 0.5を代入すると
$ C = 1 - (1 - 0.3)(1 - 0.5) = 1 - 0.7 ・ 0.5 = 1 - 0.35 = 0.65となります。
Cは A, Bより大きく、「CはA, Bよりも明るくなる」というスクリーンブレンドの特徴に満たしている結果ともいえます。
補足 : Previewノードの実際の色
シェーダーグラフ上の0.3のPreviewの色をカラーピッカーで確認してみると、0.58のような値になっています。
https://gyazo.com/fb0930f039201c3a42e8c7f7bc0c774e
これはノードの色を画面に出力する際にガンマ補正$ Out = In^{\tfrac{1.0}{2.2}}がかかるため、このような結果になっています。
$ 0.58に逆ガンマ補正 Out = In^{2.2}をかけると0.3になります。 (0.58^{2.2} \fallingdotseq 0.33)
https://gyazo.com/e8772eb4dfba8c196f4f892d3cfcd75e https://gyazo.com/8aa763ec0fb0140eb70ee25bd627fed2
参考リンク
Photoshopの描画モード(ブレンドモード)を理解するための、画像合成は計算だという話
Blend modes - Wikipedia